package com.example.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.common.Constants;
import com.example.common.enums.RoleEnum;
import com.example.entity.Account;
import com.example.service.AdminService;
import com.example.service.StudentService;
import com.example.service.TeacherService;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Optional;

/**
 * Token 工具类
 * 使用 JDK 21 优化，采用现代化 API 和 Optional
 */
@Component
public class TokenUtils {

    private static final Logger log = LoggerFactory.getLogger(TokenUtils.class);
    private static final int TOKEN_EXPIRE_HOURS = 2;

    private static AdminService staticAdminService;
    private static TeacherService staticTeacherService;
    private static StudentService staticStudentService;

    private final AdminService adminService;
    private final TeacherService teacherService;
    private final StudentService studentService;

    public TokenUtils(AdminService adminService, TeacherService teacherService, StudentService studentService) {
        this.adminService = adminService;
        this.teacherService = teacherService;
        this.studentService = studentService;
    }

    @PostConstruct
    public void init() {
        staticAdminService = adminService;
        staticTeacherService = teacherService;
        staticStudentService = studentService;
    }

    /**
     * 生成 token
     *
     * @param data 用户数据（userId-role）
     * @param sign 签名密钥（通常是用户密码）
     * @return JWT token
     */
    public static String createToken(String data, String sign) {
        LocalDateTime expireTime = LocalDateTime.now().plusHours(TOKEN_EXPIRE_HOURS);
        Date expireDate = Date.from(expireTime.atZone(ZoneId.systemDefault()).toInstant());

        return JWT.create()
                .withAudience(data) // 将 userId-role 保存到 token 里面，作为载荷
                .withExpiresAt(expireDate) // 2小时后 token 过期
                .sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥
    }

    /**
     * 获取当前登录的用户信息
     *
     * @return 当前用户账号信息，如果获取失败返回空的 Account 对象
     */
    public static Account getCurrentUser() {
        return extractTokenFromRequest()
                .flatMap(TokenUtils::parseUserFromToken)
                .orElseGet(Account::new);
    }

    /**
     * 从请求中提取 token
     */
    private static Optional<String> extractTokenFromRequest() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return Optional.empty();
            }

            HttpServletRequest request = attributes.getRequest();
            String token = request.getHeader(Constants.TOKEN);

            return StrUtil.isNotEmpty(token) ? Optional.of(token) : Optional.empty();
        } catch (Exception e) {
            log.error("提取 token 失败", e);
            return Optional.empty();
        }
    }

    /**
     * 从 token 中解析用户信息
     */
    private static Optional<Account> parseUserFromToken(String token) {
        try {
            String userRole = JWT.decode(token).getAudience().get(0);
            String[] parts = userRole.split("-");
            String userId = parts[0];
            String role = parts[1];

            Account account = switch (RoleEnum.valueOf(role)) {
                case ADMIN -> staticAdminService.selectById(Integer.valueOf(userId));
                case TEACHER -> staticTeacherService.selectById(Integer.valueOf(userId));
                case STUDENT -> staticStudentService.selectById(Integer.valueOf(userId));
            };

            return Optional.ofNullable(account);
        } catch (Exception e) {
            log.error("解析用户信息失败", e);
            return Optional.empty();
        }
    }
}

